package com.cn.xc.utils;

import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.StringWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlValue;

import org.apache.commons.io.IOUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;

import com.cn.bean.SimpleBean;
import com.cn.bean.XmlCapacity;
import com.cn.exception.MyException;
import com.cn.type.ITypeConverter;
import com.cn.type.TypeFactory;

/**
 * FUN: XML <-> bean 工具类
 *
 * @author sushy
 * @date 2016-3-23 
 */
public final class XmlBeanUtil {

	/**
	 * 输出XML
	 * 
	 * @param ele
	 * @param encoding
	 * @return
	 */
	public static String ele2Xml(Element ele, String encoding) {
	    Document doc = ele.getDocument();
        if (doc == null) {
            doc = DocumentHelper.createDocument(ele);
        }
		doc.setXMLEncoding(encoding);
		OutputFormat format = new OutputFormat();
		format.setTrimText(true);
		format.setNewlines(true); // 设置是否换行
		format.setNewLineAfterDeclaration(false);
		format.setIndent(true); // 设置是否缩进
		format.setIndent(" ");// 以空格方式实现缩进
		if (StringUtil.isNotEmpty(encoding)) {
			format.setEncoding(encoding);
		}

		StringWriter sw = new StringWriter();
		XmlWriter xmlWriter = new XmlWriter(sw, format);
		try {
			xmlWriter.write(doc);
			return sw.toString();
		} catch (IOException e) {
			throw new MyException("IO异常！");
		} finally {
			try {
				if (xmlWriter != null) {
					xmlWriter.flush();
					xmlWriter.close();
				}
			} catch (IOException e) {
			}
			IOUtils.closeQuietly(sw);
		}
	}

	/**
	 * XML转Document
	 * 
	 * @param xml
	 * @return
	 */
	public static Document xml2Doc(String xml) {
		try {
			return DocumentHelper.parseText(filterChars(xml));
		} catch (DocumentException e) {
			throw new MyException(e.getMessage());
		}
	}

	/**
	 * XML转Element
	 * 
	 * @param xml
	 * @param encoding
	 * @return
	 */
	public static Element xml2Ele(String xml) {
		Document doc = xml2Doc(filterChars(xml));
		return doc.getRootElement();
	}

	/**
	 * 序列化XmlBean
	 * 
	 * @param bean
	 * @return
	 */
	public static Element serialize(SimpleBean bean) {
		return serialize(bean, getElementName(bean));
	}

	/**
	 * 序列化XmlBean
	 * 
	 * @param bean
	 * @param localName
	 * @return
	 */
	public static Element serialize(SimpleBean bean, String localName) {
		Element localEle = DocumentHelper.createElement(localName);
		XmlType anntType = bean.getClass().getAnnotation(XmlType.class);
		if (anntType != null) {
			String[] propOrder = anntType.propOrder();
			if (propOrder != null && propOrder.length > 0
					&& StringUtil.isNotEmpty(propOrder[0])) {
				for (String prop : propOrder) {
					PropertyDescriptor pd = BeanUtil.getPropertyDescriptor(
							bean.getClass(), prop);
					if (pd != null) {
						serialize(bean, pd, localEle);
					}
				}
				return localEle;
			}
		}

		PropertyDescriptor[] pds = BeanUtil.getPropertyDescriptors(bean
				.getClass());
		for (PropertyDescriptor pd : pds) {
			serialize(bean, pd, localEle);
		}
		return localEle;
	}

	private static void serialize(SimpleBean bean, PropertyDescriptor pd,
			Element localEle) {
		if (getAnnotation(pd, XmlTransient.class) != null) {
			return;// 当前属性不序列化
		}

		Class<?> cls = pd.getPropertyType();
		if (cls == null) {
			return;
		}

		// 下一级节点
		if (SimpleBean.class.isAssignableFrom(cls)) {
			SimpleBean child = (SimpleBean) BeanUtil.getPropertyValue(bean, pd);
			if (child != null) {
				Element childEle = serialize(child, getElementName(bean, pd));
				if (childEle != null) {
					localEle.add(childEle);
				}
			}
			return;
		}

		// XML容器
		if (XmlCapacity.class.isAssignableFrom(cls)) {
			XmlCapacity capty = (XmlCapacity) BeanUtil.getPropertyValue(bean,
					pd);
			if (capty != null) {
				capty.serialize(localEle, getElementName(bean, pd));
			}
		}

		// XML元素
		if (Element.class.isAssignableFrom(cls)) {
			Element childEle = (Element) BeanUtil.getPropertyValue(bean, pd);
			if (childEle != null) {
				childEle.setParent(null);
				localEle.add(childEle);
			}
		}

		// 基本类型
		ITypeConverter type = TypeFactory.getType(cls);
		if (type == null) {
			return;// 不支持的类型直接忽略
		}

		Object value = BeanUtil.getPropertyValue(bean, pd);
		if (value == null) {
			return;
		}
		String text = type.getText(value);

		// 属性
		XmlAttribute anntAttr = getAnnotation(pd, XmlAttribute.class);
		if (anntAttr != null) {
			if (StringUtil.isNotEmpty(text)) {
				String attrName = anntAttr.name();
				if (StringUtil.isEmpty(attrName)
						|| "##default".equals(attrName)) {
					attrName = getElementName(bean, pd);
				}
				localEle.addAttribute(attrName, text);
			}
			return;
		}

		// 值
		XmlValue anntValue = getAnnotation(pd, XmlValue.class);
		if (anntValue != null) {
			if (StringUtil.isNotEmpty(text)) {
				localEle.addText(text);
			}
			return;
		}

		// 元素
		Element textEle = localEle.addElement(getElementName(bean, pd));
		if (StringUtil.isNotEmpty(text)) {
			textEle.addText(text);
		}
	}

	public static void unserialize(SimpleBean bean, Element localEle) {
		XmlType anntType = bean.getClass().getAnnotation(XmlType.class);
		if (anntType != null) {
			String[] propOrder = anntType.propOrder();
			if (propOrder != null && propOrder.length > 0
					&& StringUtil.isNotEmpty(propOrder[0])) {
				for (String prop : propOrder) {
					PropertyDescriptor pd = BeanUtil.getPropertyDescriptor(
							bean.getClass(), prop);
					if (pd != null) {
						unserialize(bean, pd, localEle);
					}
				}
				return;
			}
		}

		PropertyDescriptor[] pds = BeanUtil.getPropertyDescriptors(bean
				.getClass());
		for (PropertyDescriptor pd : pds) {
			unserialize(bean, pd, localEle);
		}
	}

	private static void unserialize(SimpleBean bean, PropertyDescriptor pd,
			Element localEle) {
		if (getAnnotation(pd, XmlTransient.class) != null) {
			return;// 当前属性不序列化
		}

		Class<?> cls = pd.getPropertyType();

		// 下一级节点
		if (SimpleBean.class.isAssignableFrom(cls)) {
			Element childEle = localEle.element(getElementName(bean, pd));
			if (childEle != null) {
				SimpleBean child = (SimpleBean) newInstance(cls);
				if (child != null) {
					unserialize(child, childEle);
					BeanUtil.setPropertyValue(bean, pd, child);
				}
			}
			return;
		}

		// XML容器
		if (XmlCapacity.class.isAssignableFrom(cls)) {
			List<?> childEles = localEle.elements(getElementName(bean, pd));
			if (childEles == null || childEles.size() <= 0) {
				return;
			}

			XmlCapacity capty = (XmlCapacity) BeanUtil.getPropertyValue(bean,
					pd);
			if (capty == null) {
				capty = (XmlCapacity) newInstance(cls);
				BeanUtil.setPropertyValue(bean, pd, capty);
			}
			if (capty != null) {
				for (int i = 0; i < childEles.size(); i++) {
					Element childEle = (Element) childEles.get(i);
					capty.unserialize(childEle);
				}
			}
		}

		// XML元素
		if (Element.class.isAssignableFrom(cls)) {
			Element childEle = localEle.element(getElementName(bean, pd));
			if (childEle != null) {
				BeanUtil.setPropertyValue(bean, pd, childEle);
			}
		}

		// 基本类型
		ITypeConverter type = TypeFactory.getType(cls);
		if (type == null) {
			return;// 不支持的类型直接忽略
		}

		// 属性
		XmlAttribute anntAttr = getAnnotation(pd, XmlAttribute.class);
		if (anntAttr != null) {
			String attrName = anntAttr.name();
			if (StringUtil.isEmpty(attrName) || "##default".equals(attrName)) {
				attrName = getElementName(bean, pd);
			}
			String text = localEle.attributeValue(attrName);
			if (StringUtil.isNotEmpty(text)) {
				Object value = type.getValue(text);
				if (value != null) {
					BeanUtil.setPropertyValue(bean, pd, value);
				}
			}
			return;
		}

		// 值
		XmlValue anntValue = getAnnotation(pd, XmlValue.class);
		if (anntValue != null) {
			String text = localEle.getText();
			if (StringUtil.isNotEmpty(text)) {
				Object value = type.getValue(text);
				if (value != null) {
					BeanUtil.setPropertyValue(bean, pd, text);
				}
			}
			return;
		}

		// 元素
		String eleName = getElementName(bean, pd);
		Element cldEle = localEle.element(eleName);
		String text = null;
		if (cldEle != null) {
			text = cldEle.getText();
			if (StringUtil.isEmpty(text) && cldEle.hasContent()) {
				text = cldEle.getStringValue();
			}
		}

		if (StringUtil.isNotEmpty(text)) {
			Object value = type.getValue(text);
			if (value != null) {
				BeanUtil.setPropertyValue(bean, pd, value);
			}
		}
	}

	/**
	 * 实例化
	 * 
	 * @param cls
	 * @return
	 */
	private static Object newInstance(Class<?> cls) {
		int mod = cls.getModifiers();
		if (Modifier.isAbstract(mod) || Modifier.isInterface(mod)) {
			return null;
		}

		try {
			return cls.newInstance();
		} catch (InstantiationException e) {
		} catch (IllegalAccessException e) {
		}

		return null;
	}

	/**
	 * 获取元素名称
	 * 
	 * @param bean
	 * @return
	 */
	public static String getElementName(SimpleBean bean) {
		String name = null;
		XmlType anntType = bean.getClass().getAnnotation(XmlType.class);
		if (anntType != null) {
			name = anntType.name();
		}
		if (StringUtil.isEmpty(name)) {
			name = bean.getClass().getSimpleName();
		}
		return name;
	}

	/**
	 * 获取元素名称(属性)
	 * 
	 * @param bean
	 * @param pd
	 * @return
	 */
	private static String getElementName(SimpleBean bean, PropertyDescriptor pd) {
		String eleName = null;
		XmlElement anntEle = getAnnotation(pd, XmlElement.class);
		if (anntEle != null) {
			eleName = anntEle.name();
		}

		if (StringUtil.isEmpty(eleName) || "##default".equals(eleName)) {
			return first2Upper(pd.getName());
		}
		return eleName;
	}

	/**
	 * 获取getter方法上的注解对象
	 * 
	 * @param bean
	 * @param pd
	 * @param annotationClass
	 * @return
	 */
	private static <T extends Annotation> T getAnnotation(
			PropertyDescriptor pd, Class<T> annotationClass) {
		try {
			// 取getter上的注解
			Method method = pd.getReadMethod();
			if (method != null) {
				return method.getAnnotation(annotationClass);
			}
		} catch (SecurityException e) {
		}
		return null;
	}

	/**
	 * 第一个字母转大写
	 * 
	 * @param str
	 * @return
	 */
	private static String first2Upper(String str) {
		if (StringUtil.isEmpty(str)) {
			return str;
		}
		return str.substring(0, 1).toUpperCase() + str.substring(1);
	}
	
    private static String filterChars(String text) {
        if (text == null) {
            return text;
        }
        
        int size = text.length();
        if (size <= 0) {
            return text;
        }
        
        StringBuilder sb = new StringBuilder(size);
        for (int i = 0; i < size; ++i) {
            char c = text.charAt(i);
            if (c == '<' || c == '>' || c == '&' || c == '\t' || c == '\n' || c == '\r') {
                sb.append(c);//保留字符
                continue;
            }
            
            if (c < 32) {
                //过滤掉[0~31]的不可见字符
                continue;
            }
            
            int v = (int) c;
            if (com.sun.org.apache.xerces.internal.util.XMLChar.isInvalid((int) c)) {//无效字符
                if (com.sun.org.apache.xerces.internal.util.XMLChar.isHighSurrogate(v)) {
                    //如果Unicode高低位匹配的就不过滤
                    if (i + 1 < size) {
                        char lowC = text.charAt(i + 1);
                        int low = (int) lowC;
                        if (com.sun.org.apache.xerces.internal.util.XMLChar.isLowSurrogate(low)) {
                            //找到低位
                            sb.append(c).append(lowC);
                            // System.out.print("high=" + (int) c);
                            // System.out.println("，low=" + low);
                            ++i;
                            continue;
                        }
                    }
                }
                
                //过滤无效字符
                //System.out.println("ignore=" + (int) c);
                continue;
            }
            
            //保留字符
            sb.append(c);
        }
        
        //System.out.println(sb);
        return sb.toString();
    }
    
    private static class XmlWriter extends XMLWriter {
        /**
         * XmlWriter.
         * 
         * @param sw
         * @param format
         * @author tqm
         * Feb 4, 2015 tqm 
         */
        public XmlWriter(StringWriter sw, OutputFormat format) {
            super(sw, format);
        }
        
        protected String escapeElementEntities(String text) {
            return super.escapeElementEntities(filterChars(text));
        }
        
        protected String escapeAttributeEntities(String text) {
            return super.escapeAttributeEntities(filterChars(text));
        }
        
    }

}
